package com.liu.activiti.service.impl;

import com.liu.activiti.exception.CustomException;
import com.liu.activiti.pojo.activiti.*;
import com.liu.activiti.service.ActivitiCoreService;
import com.liu.activiti.service.BaseActivitService;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.impl.util.CollectionUtil;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author gzj
 * @date 2024/1/10 11:02
 * @description
 */
@Slf4j
@Service
public class ActivitiCoreServiceImpl extends BaseActivitService implements ActivitiCoreService {

    /**
     * 部署流程
     *
     * @param fileResourceAddress 文件相对路径地址
     * @param processName         流程名称
     * @return
     */
    @Override
    public CustomDeployment deployByXmlFile(String fileResourceAddress, String processName) {
        log.info("deployByFile start,fileResourceAddress:{}", fileResourceAddress);
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        deploymentBuilder.addClasspathResource(fileResourceAddress);
        deploymentBuilder.name(processName);
        final Deployment deploy = deploymentBuilder.deploy();
        deploy.getDeploymentTime();
        final CustomDeployment customDeployment = new CustomDeployment();
        BeanUtils.copyProperties(deploy, customDeployment);
        log.info("deployByFile end , deployment:{}, customDeployment:{}", deploy, customDeployment);
        return customDeployment;
    }


    /**
     * 启动流程
     *
     * @param processKey  工作流定义key
     * @param businessKey 业务标识
     * @param variableMap 流程变量
     * @return
     */
    @Override
    public CustomProcessInstance startActiviti(String processKey, String businessKey, Map<String, Object> variableMap) {
        if (StringUtils.isEmpty(processKey)) throw new CustomException("工作流定义key不可为空！");
        if (StringUtils.isEmpty(businessKey)) throw new CustomException("业务id不可为空！");
        if (StringUtils.isEmpty(processKey)) throw new CustomException("工作流key不可为空！");

        CompleteTask completeTask = new CompleteTask();
        completeTask.setBusinessKey(businessKey);
        //查询是否已开启流程
        List<CustomPeddingTaskRep> taskList = peddingTaskList(completeTask);
        if (!taskList.isEmpty()) throw new CustomException("项目流程已开启不可以重复开启！");

        log.info("启动流程实例-startActiviti start, processKey:{}, businessKey:{}, variableMap:{}", processKey, businessKey, variableMap);

        //增加启动流程标识
        variableMap.put("nomalStartFlag", true);
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(processKey, businessKey, variableMap);
        CustomProcessInstance customProcessInstance = new CustomProcessInstance();
        BeanUtils.copyProperties(instance, customProcessInstance);
        log.info("启动流程实例-startActiviti end, instance:{}, customProcessInstance:{}", instance, customProcessInstance);
        return customProcessInstance;
    }


    /**
     * 结束流程
     *
     * @param completeTask 任务ID
     * @return
     */
    @Override
    public Boolean forceEnd(CompleteTask completeTask) {
        log.info("撤销:强制结束一个流程-forceEnd start, completeTask:{}", completeTask);
        List<CustomPeddingTaskRep> customPeddingTaskReps = peddingTaskList(completeTask);
        if(customPeddingTaskReps.isEmpty()){
            throw new CustomException("当前流程已结束，无需结束！");
        }
        boolean flag = false;
        CustomPeddingTaskRep peddingTaskRep = customPeddingTaskReps.get(0);
        String processDefinitionId = runtimeService.createProcessInstanceQuery().processInstanceId(peddingTaskRep.getProcessInstanceId()).singleResult().getProcessDefinitionId();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        // 寻找流程实例当前任务的activeId
        Execution execution = runtimeService.createExecutionQuery().executionId(peddingTaskRep.getExecutionId()).singleResult();
        String activityId = execution.getActivityId();
        FlowNode currentNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
        // 创建结束节点和连接线
        EndEvent end = new EndEvent();
        end.setName("强制结束");
        end.setId("forceEnd");
        List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newFlow");
        newSequenceFlow.setSourceFlowElement(currentNode);
        newSequenceFlow.setTargetFlowElement(end);
        newSequenceFlowList.add(newSequenceFlow);
        // 备份原有方向
        List<SequenceFlow> dataflows = currentNode.getOutgoingFlows();
        List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
        oriSequenceFlows.addAll(dataflows);
        // 清空原有方向
        currentNode.getOutgoingFlows().clear();
        // 设置新方向
        currentNode.setOutgoingFlows(newSequenceFlowList);

        // 完成当前任务 可能存在多例任务
        for (CustomPeddingTaskRep customPeddingTaskRep :customPeddingTaskReps) {
            taskService.addComment(customPeddingTaskRep.getTaskId(), customPeddingTaskRep.getProcessInstanceId(), "comment", "撤销流程");
            taskService.complete(customPeddingTaskRep.getTaskId());
        }

        // 恢复原有方向
        currentNode.setOutgoingFlows(oriSequenceFlows);
        log.info("撤销:强制结束一个流程-forceEnd end");
        return flag;
    }

    /**
     * 查询当前 任务列表
     *
     * @param completeTask 待处理任务实体
     * @return
     */
    @Override
    public List<CustomPeddingTaskRep> peddingTaskList(CompleteTask completeTask) {
        log.info("查询待处理任务-peddingTaskList start, completeTask:{}", completeTask);

        final TaskQuery taskQuery = taskService.createTaskQuery();
        //任务指定用户人
        if (StringUtils.isNotBlank(completeTask.getAssignee())) {
            taskQuery.taskAssignee(completeTask.getAssignee());
        }
        //任务候选人
        if (StringUtils.isNotBlank(completeTask.getCandidateUser())) {
            taskQuery.taskCandidateUser(completeTask.getCandidateUser());
        }
        //候选组
        if (StringUtils.isNotBlank(completeTask.getCandidateGroup())) {
            taskQuery.taskCandidateGroup(completeTask.getCandidateGroup());
        }
        //业务标识
        if (StringUtils.isNotBlank(completeTask.getBusinessKey())) {
            taskQuery.processInstanceBusinessKey(completeTask.getBusinessKey());
        }
        //流程实例ID
        if (StringUtils.isNotBlank(completeTask.getProcessInstanceId())) {
            taskQuery.processInstanceId(completeTask.getProcessInstanceId());
        }

        final List<Task> list = taskQuery.orderByTaskCreateTime().desc().list();
        log.info("查询原生流程返回结果list:{}", list);
        final ArrayList<CustomPeddingTaskRep> taskReps = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(list)) {
            for (Task task : list) {
                final CustomPeddingTaskRep customPeddingTaskRep = new CustomPeddingTaskRep();
                customPeddingTaskRep.setTaskId(task.getId());
                customPeddingTaskRep.setTaskName(task.getName());
                customPeddingTaskRep.setTaskAssiginee(task.getAssignee());
                customPeddingTaskRep.setExecutionId(task.getExecutionId());
                customPeddingTaskRep.setProcessDefinitionId(task.getProcessDefinitionId());
                customPeddingTaskRep.setProcessInstanceId(task.getProcessInstanceId());
                taskReps.add(customPeddingTaskRep);
            }
        }
        log.info("查询待处理任务-peddingTaskList end, taskReps:{}", taskReps);
        return taskReps;
    }


    /**
     * 任务完成
     *
     * @param completeTask 任务实例对象
     * @return
     */
    @Override
    public Boolean completeTask(CompleteTask completeTask) {
        if (StringUtils.isEmpty(completeTask.getBusinessKey())) throw new CustomException("业务id不可为空！");
        if (StringUtils.isEmpty(completeTask.getAssignee()) && StringUtils.isEmpty(completeTask.getCandidateUser()))
            throw new CustomException("完成用户的id不可为空！");
        if (StringUtils.isEmpty(completeTask.getProcessKey())) throw new CustomException("工作流key不可为空！");

        //查询代办的任务id
        TaskQuery taskQuery = taskService.createTaskQuery()
                .processDefinitionKey(completeTask.getBusinessKey())
                .processInstanceBusinessKey(completeTask.getProcessKey());

        //查询固定任务用户id
        if (StringUtils.isNotEmpty(completeTask.getAssignee())) {
            taskQuery.taskAssignee(completeTask.getAssignee());
        }
        //查询候选人用户id
        if (StringUtils.isNotEmpty(completeTask.getCandidateUser())) {
            taskQuery.taskCandidateUser(completeTask.getCandidateUser());
        }

        List<Task> list = taskQuery.list();
        if (list.isEmpty()) {
            throw new CustomException("当前专家没有可完成的任务！");
        }
        Task task = list.get(0);
        //设置备注
        if (StringUtils.isNotBlank(completeTask.getMessage())) {
            taskService.addComment(task.getId(), null, completeTask.getMessage());
        }
        //候选人领取任务 如果候选任务没有进行领取就直接完成的话，那么在历史记录中就不会记录是哪个用户执行了这个任务.
        if (StringUtils.isNotEmpty(completeTask.getCandidateUser())) {
            taskService.claim(task.getId(), completeTask.getCandidateUser());
        }
        //完成任务
        Map<String, Object> variables = new HashMap<>();
        variables.put("nomalStartFlag", true);
        taskService.complete(task.getId(), variables);
        log.info("完成工作流任务-end");
        return true;
    }


    @Override
    public Boolean reBackTask(CompleteTask completeTask, String previousDefinitionKey, Map<String, Object> variables) {
        log.info("reBackTask start,completeTask:{}, previousDefinitionKey:{}", completeTask, previousDefinitionKey);
        List<CustomPeddingTaskRep> customPeddingTaskReps = peddingTaskList(completeTask);
        if(customPeddingTaskReps.isEmpty()){
            throw new CustomException("当前流程已结束，无法退回！");
        }
        CustomPeddingTaskRep customPeddingTaskRep = customPeddingTaskReps.get(0);
        String processDefinitionId = runtimeService.createProcessInstanceQuery().processInstanceId(customPeddingTaskRep.getProcessInstanceId()).singleResult().getProcessDefinitionId();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        // 寻找流程实例当前任务的activeId
        Execution execution = runtimeService.createExecutionQuery().executionId(customPeddingTaskRep.getExecutionId()).singleResult();
        String activityId = execution.getActivityId();
        FlowNode currentNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
        FlowNode targetNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(previousDefinitionKey);
        // 创建连接线
        List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newFlow");
        newSequenceFlow.setSourceFlowElement(currentNode);
        newSequenceFlow.setTargetFlowElement(targetNode);
        newSequenceFlowList.add(newSequenceFlow);
        // 备份原有方向
        List<SequenceFlow> dataflows = currentNode.getOutgoingFlows();
        List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
        oriSequenceFlows.addAll(dataflows);
        // 清空原有方向
        currentNode.getOutgoingFlows().clear();
        // 设置新方向
        currentNode.setOutgoingFlows(newSequenceFlowList);
        // 完成当前任务
        for (CustomPeddingTaskRep peddingTaskRep:customPeddingTaskReps) {
            taskService.addComment(peddingTaskRep.getTaskId(), peddingTaskRep.getProcessInstanceId(), "reback", "回退节点");
            variables.put("nomalStartFlag", false);
            taskService.complete(peddingTaskRep.getTaskId(), variables);
        }
        // 恢复原有方向
        currentNode.setOutgoingFlows(oriSequenceFlows);
        log.info("reBackTask end");
        return true;
    }


    @Override
    public TreeNode getProcessNodeInfo(String processDefinitionId, String processInstanceId) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        final Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
        final List<UserTask> usertaskFlowElements = new ArrayList<>();
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof UserTask) {
                usertaskFlowElements.add((UserTask) flowElement);
            }
        }
        final TreeNode treeNode = buildTree(bpmnModel, processDefinitionId, processInstanceId);
        return treeNode;
    }

    @Override
    public List<CustomProcessNode> getProcessCurrentNodeInfo(String processInstanceId) {

        // 获取当前任务（如果活动节点是用户任务）
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .list();
        final ArrayList<CustomProcessNode> nodes = new ArrayList<>();
        for (Task task : tasks) {
            final CustomProcessNode customProcessNode = new CustomProcessNode();
            customProcessNode.setProcessNodeName(task.getName());
            customProcessNode.setProcessNodeDefinitionKey(task.getTaskDefinitionKey());
            customProcessNode.setState(ProcessStateEnum.RUNNING);
            customProcessNode.setNodeTypeEnum(NodeTypeEnum.AUDIT_NODE);
            System.out.println("当前任务ID：" + task.getId());
            System.out.println("当前任务名称：" + task.getName());
            System.out.println("任务的流程实例ID：" + task.getProcessInstanceId());
            System.out.println("任务的执行ID：" + task.getExecutionId());
            nodes.add(customProcessNode);
        }
        return nodes;
    }

    @Override
    public TableDataInfo listHistoryProcess(HistoryProcessReq historyProcessReq) {
        int start = (historyProcessReq.getPageNumber() - 1) * historyProcessReq.getPageSize();
        HistoricProcessInstanceQuery condition = historyService.createHistoricProcessInstanceQuery();
        if (StringUtils.isNotEmpty(historyProcessReq.getBussinesskey())) {
            condition.processInstanceBusinessKey(historyProcessReq.getBussinesskey());
        }
        if (StringUtils.isNotEmpty(historyProcessReq.getProcessInstanceId())) {
            condition.processInstanceId(historyProcessReq.getProcessInstanceId());
        }
        int total = condition.orderByProcessInstanceStartTime().desc().list().size();
        List<HistoricProcessInstance> processList = condition.orderByProcessInstanceStartTime().desc().listPage(start, historyProcessReq.getPageSize());
        List<FlowInfo> flows = new ArrayList<>();
        processList.stream().forEach(p -> {
            FlowInfo info = new FlowInfo();
            info.setId(p.getId());
            info.setProcessInstanceId(p.getId());
            info.setBusinessKey(p.getBusinessKey());
            info.setName(p.getProcessDefinitionName());
            info.setStartTime(p.getStartTime());
            info.setEndTime(p.getEndTime());
            info.setStartUserId(p.getStartUserId());
            if (p.getEndTime() == null) {
                info.setEnded(false);
                // 查看当前活动任务
                List<Task> tasks = taskService.createTaskQuery().processInstanceId(p.getId()).list();
                if (tasks.size() > 0) {
                    String taskName = "";
                    String assignee = "";
                    for (Task t : tasks) {
                        taskName += t.getName() + ",";
                        assignee += t.getAssignee() + ",";
                    }
                    taskName = taskName.substring(0, taskName.length() - 1);
                    assignee = assignee.substring(0, assignee.length() - 1);
                    info.setCurrentTask(taskName);
                    info.setAssignee(assignee);
                }
            } else {
                info.setEnded(true);
            }
            flows.add(info);
        });
        TableDataInfo rspData = new TableDataInfo();
        rspData.setRows(flows);
        rspData.setTotal(total);
        return rspData;
    }


    /**
     * 获取所有排序后的用户任务节点
     *
     * @param model bpmnModel对象
     * @return 用户任务节点列表
     */
    public static List<UserTask> getAllSortedUserTaskEvent(BpmnModel model) {
        final Collection<UserTask> userTasks = getAllUserTaskEvent(model);
        return sortTasksByFlowPath(getStartEvent(model), userTasks);
    }

    /**
     * 获取开始节点
     *
     * @param model bpmnModel对象
     * @return 开始节点（未找到开始节点，返回null）
     */
    public static StartEvent getStartEvent(BpmnModel model) {
        Process process = model.getMainProcess();
        FlowElement startElement = process.getInitialFlowElement();
        if (startElement instanceof StartEvent) {
            return (StartEvent) startElement;
        }
        return null;
    }

    /**
     * 获取所有用户任务节点
     *
     * @param model bpmnModel对象
     * @return 用户任务节点列表
     */
    public static Collection<UserTask> getAllUserTaskEvent(BpmnModel model) {
        Process process = model.getMainProcess();
        Collection<FlowElement> flowElements = process.getFlowElements();
        return getAllUserTaskEvent(flowElements, null);
    }

    /**
     * 获取所有用户任务节点
     *
     * @param flowElements 流程元素集合
     * @param allElements  所有流程元素集合
     * @return 用户任务节点列表
     */
    public static Collection<UserTask> getAllUserTaskEvent(Collection<FlowElement> flowElements, Collection<UserTask> allElements) {
        allElements = allElements == null ? new ArrayList<>() : allElements;
        for (FlowElement flowElement : flowElements) {

            if (flowElement instanceof UserTask) {
                allElements.add((UserTask) flowElement);
            }
            if (flowElement instanceof SubProcess) {
                // 继续深入子流程，进一步获取子流程
                allElements = getAllUserTaskEvent(((SubProcess) flowElement).getFlowElements(), allElements);
            }
        }
        return allElements;
    }

    /**
     * 对任务节点按照流程图顺序排序
     *
     * @param startEvent 开始节点（根节点）
     * @param userTasks  所有任务节点
     * @return
     */
    public static List<UserTask> sortTasksByFlowPath(StartEvent startEvent, Collection<UserTask> userTasks) {
        List<UserTask> sorted = new ArrayList<>(userTasks.size());
        FlowElement next = startEvent.getOutgoingFlows().get(0).getTargetFlowElement();
        if (next == null) {
            throw new RuntimeException("流程图开始节点未找到目标节点");
        }
        // 第一个节点
        sorted.add((UserTask) next);
        boolean end = false;
        while (!end) {
            next = findTargetNode(next);
            if (next instanceof Gateway) {
                continue;
            }
            if (next instanceof EndEvent) {
                end = true;
                continue;
            }
            if (next == null) {
                end = true;
            } else {
                sorted.add((UserTask) next);
            }
        }

        return sorted;
    }

    /**
     * 查找节点在工作流中的下一个节点。如遇到网关有多个出口路径，则只搜索条件表达式包含‘pass’的路径。
     *
     * @param node
     * @return
     */
    private static FlowElement findTargetNode(FlowElement node) {
        if (node instanceof Gateway) {
            SequenceFlow passWay = ((Gateway) node).getOutgoingFlows().stream()
                    .filter(flow -> flow.getConditionExpression().contains("pass"))
                    .findFirst()
                    .orElseThrow(() -> new RuntimeException("网关未正确配置流转条件"));
            return passWay.getTargetFlowElement();
        }
        if (node instanceof EndEvent) {
            return null;
        }
        if (node == null) {
            return null;
        }
        final UserTask userTask = (UserTask) node;
        // 用户任务节点默认只支持一个出口路径
        return userTask.getOutgoingFlows().get(0).getTargetFlowElement();
    }

    // 将BPMN模型中的节点转换为多叉树
    public TreeNode buildTree(BpmnModel bpmnModel, String processDefinitionId, String processInstanceId) {
        if (bpmnModel == null) {
            return null;
        }
        Process mainProcess = bpmnModel.getMainProcess();
        Collection<FlowElement> flowElements = mainProcess.getFlowElements();
        Map<String, TreeNode> nodeMap = new HashMap<>();
        TreeNode root = null;
        // 初始化所有节点
        for (FlowElement flowElement : flowElements) {
            final CustomProcessNode customProcessNode = buildCustomProcessNode(flowElement, processDefinitionId, processInstanceId);
            TreeNode node = new TreeNode(flowElement.getId(), customProcessNode);
            nodeMap.put(node.id, node);
            if (flowElement instanceof StartEvent) {
                root = node; // 根节点是开始事件
            }
        }
        // 处理连接线，建立节点之间的关系
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof SequenceFlow) {
                SequenceFlow sequenceFlow = (SequenceFlow) flowElement;
                TreeNode sourceNode = nodeMap.get(sequenceFlow.getSourceRef());
                TreeNode targetNode = nodeMap.get(sequenceFlow.getTargetRef());
                if (sourceNode != null && targetNode != null) {
                    sourceNode.addChild(targetNode);
                }
            }
        }
        return root;
    }

    private CustomProcessNode buildCustomProcessNode(FlowElement flowElement, String processDefinitionId, String processInstanceId) {
        CustomProcessNode customProcessTask = new CustomProcessNode();
        customProcessTask.setProcessNodeDefinitionKey(flowElement.getId());
        customProcessTask.setProcessNodeName(flowElement.getName());
//         customProcessTask.setAssiginee(flowElement.getAssignee());
        //过滤已经跑完的用户任务
        //获取跑完的流程节点
        // 设置节点类型
        if (flowElement instanceof StartEvent) {
            customProcessTask.setNodeTypeEnum(NodeTypeEnum.START_NODE);
        }
        if (flowElement instanceof EndEvent) {
            customProcessTask.setNodeTypeEnum(NodeTypeEnum.END_NODE);
        }
        if (flowElement instanceof UserTask) {
            customProcessTask.setNodeTypeEnum(NodeTypeEnum.AUDIT_NODE);
        }
        if (flowElement instanceof SequenceFlow) {
            customProcessTask.setNodeTypeEnum(NodeTypeEnum.CONDITIONAL_BRANCH_NODE);
        }
        final HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
        final List<HistoricActivityInstance> list = historicActivityInstanceQuery
                .processDefinitionId(processDefinitionId).processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().desc().list();
        final List<HistoricActivityInstance> taskInstances = list.stream().filter(task -> flowElement.getId().equals(task.getActivityId())).sorted(Comparator.comparing(HistoricActivityInstance::getStartTime).reversed()).collect(Collectors.toList());

        if (CollectionUtil.isEmpty(taskInstances)) {
            customProcessTask.setState(ProcessStateEnum.NOT_START);
            return customProcessTask;
        }
        HistoricActivityInstance historicTaskInstance = taskInstances.get(0);

        if (historicTaskInstance == null) {
            customProcessTask.setState(ProcessStateEnum.NOT_START);
        }
        if (historicTaskInstance != null && historicTaskInstance.getEndTime() == null) {
            customProcessTask.setState(ProcessStateEnum.RUNNING);
        }
        if (historicTaskInstance != null && historicTaskInstance.getEndTime() != null) {
            customProcessTask.setState(ProcessStateEnum.PASS);
        }
        return customProcessTask;
    }
}

